package com.sm.gateway.filter;

/**
 * @program: practice
 * @description:
 * @author: liqitian.
 * @create: 2019-11-15 14:39
 **/


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.sm.gateway.feigns.UserFeignClient;
import com.sm.tool.entity.response.ReturnData;
import com.sm.tool.enums.NumberEnums;
import com.sm.tool.enums.ResultEnums;

import com.sm.tool.utils.JwtUtil;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import redis.clients.jedis.JedisCluster;

import java.nio.charset.StandardCharsets;


/**
 * 描述: 权限过滤器（暂时未考虑到adminToken）
 *
 * @Auther: lzx
 * @Date: 2019/7/9 15:49
 */
@Component
//读取 yml 文件下的 ky
@ConfigurationProperties("ky")
@Slf4j
@Getter
@Setter
public class AuthTokenFilter implements GlobalFilter, Ordered {

    private String[] skipAuthUrls;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private JedisCluster jedisCluster;

    @Autowired
    private UserFeignClient userFeignClient;


    private static Gson gson = new Gson();

    /**
     * 过滤器
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getURI().getPath();

        if (null != skipAuthUrls && skipAuthUrls.length != 0){
            for (String skipAuthUrl:skipAuthUrls) {
                if (url.contains(skipAuthUrl)){
                    return chain.filter(exchange);
                }
            }
        }

        HttpHeaders httpHeaders = exchange.getRequest().getHeaders();
        //获取token
        String token = httpHeaders.getFirst("token");
        String adminToken = httpHeaders.getFirst("adminToken");

        ServerHttpResponse resp = exchange.getResponse();
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        if (StringUtils.isBlank(token) && StringUtils.isBlank(adminToken)) {
            //没有token
            return authError(resp, ResultEnums.PLEARSE_LOGIN.getCode(), ResultEnums.PLEARSE_LOGIN.getMessage());
        } else {
            if (token != null){
                /**
                 * 用户验证
                 */
                String userId = jedisCluster.get(token);
                if (userId == null) {
                    //重新登录
                    return authError(resp, ResultEnums.ACCOUNT_EXPIRED.getCode(), ResultEnums.ACCOUNT_EXPIRED.getMessage());
                } else {
                    //方便jj测试 这里不往头部放userId了

                    // 向headers中放文件，记得build
                    //ServerHttpRequest host = exchange.getRequest().mutate().header("userId", userId).build();
                    //将现在的request 变成 change对象
                    //ServerWebExchange build = exchange.mutate().request(host).build();
                    return chain.filter(exchange);
                    //return chain.filter(build);
                }
            }else if (adminToken != null){
                String adminUserId = jedisCluster.get(adminToken);
                if (adminUserId == null){
                    //重新登录
                    return authError(resp, ResultEnums.ACCOUNT_EXPIRED.getCode(), ResultEnums.ACCOUNT_EXPIRED.getMessage());
                }else {
                    try {
                        JwtUtil.parseToken(adminToken);
                    } catch (SignatureException se) {
                        // TODO 这里自行抛出异常
                        log.error("===== 密钥不匹配 =====", se);
                        return authError(resp, ResultEnums.LOGIN_EXCEPTION.getCode(), ResultEnums.LOGIN_EXCEPTION.getMessage());
                    } catch (ExpiredJwtException ejw) {
                        // TODO 这里自行抛出异常
                        log.error("===== token过期 =====", ejw);
                        return authError(resp, ResultEnums.TOKEN_EXPIRED.getCode(), ResultEnums.TOKEN_EXPIRED.getMessage());
                    } catch (Exception e) {
                        // TODO 这里自行抛出异常
                        log.error("===== token解析异常 =====", e);
                        return authError(resp, ResultEnums.LOGIN_EXCEPTION.getCode(), ResultEnums.LOGIN_EXCEPTION.getMessage());
                    }

                    Boolean flag = userFeignClient.check_exist_admin(Long.parseLong(adminUserId));
                    if (!flag){
                        return authError(resp, ResultEnums.PERMISSION_EXCEPTION.getCode(), ResultEnums.PERMISSION_EXCEPTION.getMessage());
                    }
                    return chain.filter(exchange);
                }

            }
        }
        //重新登录
        return authError(resp, ResultEnums.ACCOUNT_EXPIRED.getCode(), ResultEnums.ACCOUNT_EXPIRED.getMessage());
    }

    /**
     * 认证错误输出
     *
     * @param resp 响应对象
     * @param mess 错误信息
     * @return
     */
    private Mono<Void> authError(ServerHttpResponse resp, int code, String mess) {
        resp.setStatusCode(HttpStatus.UNAUTHORIZED);
        ReturnData returnData = ReturnData.builder().code(code).msg(mess).build();
        String returnStr = "";
        try {
            returnStr = new ObjectMapper().writeValueAsString(returnData);
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
        }
        DataBuffer buffer = resp.bufferFactory().wrap(returnStr.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Flux.just(buffer));
    }

    @Override
    public int getOrder() {
        return -100;
    }
}

